using Force.DeepCloner;
using Microsoft.AspNetCore.Components;
using StudentVideo.Client.Services;

namespace StudentVideo.Client.Components;

public record TabActiveChangeEventArgs(string NewValue, string? OldValue);

/// <summary>
/// 删除元素参数
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Items">删除后的列表</param>
/// <param name="Target">被删除的元素</param>
/// <param name="next">删除后激活的元素</param>
public record TabRemoveClickEventArgs<T>(List<T> Items, T Target, T? next);

public partial class CuTabs<TItem>: ComponentBase {
   [Inject] public required JsInteropService _jsInterop { get; set; }

   [Parameter] public string? ActiveId { get; set; }

   private List<TItem> _items = [];

   [Parameter] public required List<TItem> Items { get; set; }

   [Parameter] public required Func<TItem, string> ItemKey { get; set; }
   [Parameter] public required Func<TItem, string> ItemName { get; set; }

   [Parameter] public bool RemoveButton { get; set; } = false;

   [Parameter] public EventCallback<TabRemoveClickEventArgs<TItem>> OnRemoveClick { get; set; }
   [Parameter] public EventCallback<TItem> OnItemClick { get; set; }
   [Parameter] public EventCallback<TabActiveChangeEventArgs> ActiveChange { get; set; }

   [Parameter] public string Class { get; set; } = string.Empty;

   /// <summary> item 的 css class </summary>
   [Parameter] public string ItemClass { get; set; } = string.Empty;

   private class ItemData {
      public required string Id { get; set; }
      public required double Width { get; set; }
   }

   private List<ItemData> ItemDataList { get; set; } = [];
   private double MarkLeft { get; set; } = 0;
   private double MarkWidth { get; set; } = 0;

   protected override async Task OnAfterRenderAsync(bool firstRender) {
      await base.OnAfterRenderAsync(firstRender);

      if (ItemsChanged()) {
         await UpdateItemDataList();
         MoveMark();
         StateHasChanged();
      }
   }

   private bool ItemsChanged() {
      bool change;
      change = !_items.SequenceEqual(Items);

      _items = Items;

      return change;
   }

   private async Task UpdateItemDataList() {
      var tasks = Items.Select(async item => {
         var id = ItemKey(item);
         var width = await _jsInterop.GetElementProperty<double>($"#{id}", "clientWidth");
         return new ItemData { Id = id, Width = width };
      }).ToList();

      ItemDataList = [.. await Task.WhenAll(tasks)];
   }

   /// <summary> 元素删除 </summary>
   private async Task RemoveClick(TItem item) {
      int itemIndex = Items.IndexOf(item);

      Items.Remove(item);

      TItem? newItem = default;

      if (ActiveId == ItemKey(item)) {
         if (itemIndex == Items.Count)
            itemIndex--;

         newItem = Items[itemIndex];

         await UpdateActiveId(ItemKey(newItem));
      }

      await UpdateItemDataList();
      MoveMark();
      StateHasChanged();

      if (OnRemoveClick.HasDelegate)
         await OnRemoveClick.InvokeAsync(new TabRemoveClickEventArgs<TItem>(Items, item, newItem));
   }

   /// <summary> 元素点击 </summary>
   private async Task ItemClick(TItem item) {
      var id = ItemKey(item);
      if (id == ActiveId)
         return;

      // 先触发点击事件
      if (OnItemClick.HasDelegate)
         await OnItemClick.InvokeAsync(item);

      // 再检查是否需要切换激活状态
      if (id != ActiveId)
         await UpdateActiveId(id);

      MoveMark();
   }

   private async Task UpdateActiveId(string newValue) {
      var oldValue = ActiveId;
      ActiveId = newValue;

      // 触发自定义的ActiveChange事件
      if (ActiveChange.HasDelegate)
         await ActiveChange.InvokeAsync(new TabActiveChangeEventArgs(newValue, oldValue));
   }

   private void MoveMark() {
      if (ActiveId == null)
         return;

      try {
         var current = ItemDataList.Find(e => e.Id == ActiveId);
         if (current == null)
            return;

         var index = ItemDataList.IndexOf(current);

         var leftList = ItemDataList[..index];

         double left = 0;
         foreach (var item in leftList) {
            left += item.Width;
         }

         MarkWidth = current.Width / 2;
         MarkLeft = left + (MarkWidth / 2) + (index * 7);
      } catch (Exception ex) {
         Console.WriteLine(ex);
      }
   }
}
